﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace iNet.Media.Imaging.Filters
{
	/// <summary>
	/// 色階等化濾鏡工作。
	/// </summary>
	public class HistogramEqualizationFilterTask : SimpleColorTransformFilterTask
	{
		#region 欄位
		double _Strength = 0.5;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 HistogramEqualizationFilterTask 執行個體。
		/// </summary>
		public HistogramEqualizationFilterTask()
		{ }
		/// <summary>
		/// 初始化新的 HistogramEqualizationFilterTask 執行個體。
		/// </summary>
		/// <param name="sourceBitmap">來源點陣圖。</param>
		/// <param name="strength">等化強度，範圍為 0 ~ 1，0 表示最弱；1 表示最強。</param>
		/// <exception cref="ArgumentOutOfRangeException">strength 不是範圍內的值。</exception>
		public HistogramEqualizationFilterTask(IBitmapImage sourceBitmap, double strength)
			: base(sourceBitmap)
		{
			if (strength < 0 || strength > 1)
				throw new ArgumentOutOfRangeException("");
			_Strength = strength;
		}
		#endregion
		#region ApplyFilter
		/// <summary>
		/// 套用濾鏡至點陣圖。
		/// </summary>
		/// <param name="version">工作版本。</param>
		/// <param name="filterData">套用濾鏡所需的資料。</param>
		/// <returns>True 表示濾鏡套用成功或取消，False 表示失敗。</returns>
		protected unsafe override bool ApplyFilter(Tasks.TaskVersion version, BitmapImageFilterData filterData)
		{
			//取得濾鏡資料
			BitmapImageFilterChannels channels = this.Channels;
			double strength;
			if (!this.ReadValue<double>(version, ref _Strength, out strength))
				return true;

			//建立轉換表
			if (strength > 0)
			{
				//取得點陣圖資訊
				bool is16bitsColor = filterData.Is16BitsColor;
				bool isAlphaIncluded = (channels != BitmapImageFilterChannels.Luminance && filterData.IsAlphaIncluded);
				int bytesPerPixel = filterData.BytesPerPixel;
				int stride = filterData.Stride;
				int width = filterData.PixelWidth;
				int height = filterData.PixelHeight;
				int colorCount = (is16bitsColor ? 65536 : 256);
				int pixelCount = (width * height);
				byte* srcPixels = (byte*)filterData.SourcePixelBuffer;

				//統計色階分佈
				int[] histogramI = null;
				int[] histogramA = null;
				int[] histogramR = null;
				int[] histogramG = null;
				int[] histogramB = null;
				if (channels == BitmapImageFilterChannels.Luminance)
					histogramI = new int[colorCount];
				else
				{
					if (isAlphaIncluded)
						histogramA = new int[colorCount];
					histogramR = new int[colorCount];
					histogramG = new int[colorCount];
					histogramB = new int[colorCount];
				}
				this.EnumerateSourceBitmapArgb(filterData, (x, y, a, r, g, b) =>
				{
					if (histogramI != null)
						++histogramI[ColorConversion.ToLuminanceUnsafe(r, g, b)];
					else
					{
						if (isAlphaIncluded)
							++histogramA[a];
						++histogramR[r];
						++histogramG[g];
						++histogramB[b];
					}
					if ((y % 100) == 99)
					{
						this.ReportProgress(version, (y * 50.0 / height));
						if (this.IsCanceling)
							return false;
					}
					return true;
				});
				this.ReportProgress(version, 50);

				//中斷確認
				if (this.IsCanceling)
					return true;

				//計算機率分佈
				double[] probabilitiesI = (histogramI != null ? new double[colorCount] : null);
				double[] probabilitiesA = (histogramA != null ? new double[colorCount] : null);
				double[] probabilitiesR = (histogramR != null ? new double[colorCount] : null);
				double[] probabilitiesG = (histogramG != null ? new double[colorCount] : null);
				double[] probabilitiesB = (histogramB != null ? new double[colorCount] : null);
				for (int i = colorCount - 1; i >= 0; --i)
				{
					if (probabilitiesI != null)
						probabilitiesI[i] = (double)histogramI[i] / pixelCount;
					else
					{
						if (probabilitiesA != null)
							probabilitiesA[i] = (double)histogramA[i] / pixelCount;
						probabilitiesR[i] = (double)histogramR[i] / pixelCount;
						probabilitiesG[i] = (double)histogramG[i] / pixelCount;
						probabilitiesB[i] = (double)histogramB[i] / pixelCount;
					}
				}

				//建立轉換表
				int maxColorDiff = (int)(strength * colorCount) / 2;
				if (channels == BitmapImageFilterChannels.Luminance)
				{
					int[] table = new int[colorCount];
					double accumulation = 0;
					for (int i = 0; i < colorCount; ++i)
					{
						accumulation += probabilitiesI[i];
						int newColor = (int)(colorCount * accumulation);
						int minColor = Math.Max(0, i - maxColorDiff);
						int maxColor = Math.Min(colorCount - 1, i + maxColorDiff);
						newColor = Math.Min(Math.Max(newColor, minColor), maxColor);
						table[i] = newColor;
					}
					this.LuminanceTransformTable = table;
				}
				else
				{
					int[] tableA = (probabilitiesA != null ? new int[colorCount] : null);
					int[] tableR = new int[colorCount];
					int[] tableG = new int[colorCount];
					int[] tableB = new int[colorCount];
					double accumulationA = 0;
					double accumulationR = 0;
					double accumulationG = 0;
					double accumulationB = 0;
					for (int i = 0; i < colorCount; ++i)
					{
						// Alpha
						int newColor, minColor, maxColor;
						if (probabilitiesA != null)
						{
							accumulationA += probabilitiesA[i];
							newColor = (int)(colorCount * accumulationA);
							minColor = Math.Max(0, i - maxColorDiff);
							maxColor = Math.Min(colorCount - 1, i + maxColorDiff);
							newColor = Math.Min(Math.Max(newColor, minColor), maxColor);
							tableA[i] = newColor;
						}

						//紅色
						accumulationR += probabilitiesR[i];
						newColor = (int)(colorCount * accumulationR);
						minColor = Math.Max(0, i - maxColorDiff);
						maxColor = Math.Min(colorCount - 1, i + maxColorDiff);
						newColor = Math.Min(Math.Max(newColor, minColor), maxColor);
						tableR[i] = newColor;

						//綠色
						accumulationG += probabilitiesG[i];
						newColor = (int)(colorCount * accumulationG);
						minColor = Math.Max(0, i - maxColorDiff);
						maxColor = Math.Min(colorCount - 1, i + maxColorDiff);
						newColor = Math.Min(Math.Max(newColor, minColor), maxColor);
						tableG[i] = newColor;

						//藍色
						accumulationB += probabilitiesB[i];
						newColor = (int)(colorCount * accumulationB);
						minColor = Math.Max(0, i - maxColorDiff);
						maxColor = Math.Min(colorCount - 1, i + maxColorDiff);
						newColor = Math.Min(Math.Max(newColor, minColor), maxColor);
						tableB[i] = newColor;
					}
					this.AlphaTransformTable = tableA;
					this.RedColorTransformTable = tableR;
					this.GreenColorTransformTable = tableG;
					this.BlueColorTransformTable = tableB;
				}
			}

			//套用濾鏡
			return base.ApplyFilter(version, filterData);
		}
		#endregion
		#region Strength
		/// <summary>
		/// 取得或設定等化強度，範圍為 0 ~ 1，0 表示最弱；1 表示最強，預設值為 0.5。
		/// </summary>
		public double Strength
		{
			get
			{
				return _Strength;
			}
			set
			{
				if (value < 0 || value > 1)
					throw new ArgumentOutOfRangeException();
				this.SetInitializingParameter<double>(ref _Strength, value);
			}
		}
		#endregion
	}
}
